package com.tsugun.mediaplayer.presenter.provider;

import android.content.Context;
import android.os.AsyncTask;

import com.tsugun.mediaplayer.R;
import com.tsugun.mediaplayer.model.database.DBOperator;
import com.tsugun.mediaplayer.model.database.DataOperator;
import com.tsugun.mediaplayer.model.entity.AlbumModel;
import com.tsugun.mediaplayer.model.entity.ArtistModel;
import com.tsugun.mediaplayer.model.entity.DownloadMusicModel;
import com.tsugun.mediaplayer.model.entity.MusicModel;
import com.tsugun.mediaplayer.model.entity.SheetModel;
import com.tsugun.mediaplayer.model.source.LocalMusicSource;
import com.tsugun.mediaplayer.model.source.MusicSource;
import com.tsugun.mediaplayer.presenter.utils.ConverterHelper;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 音乐列表内容提供者
 * Created by shize on 2017/3/29.
 */

public class MusicListProvider implements ListProvider {
    // provider的状态
    public static final short STATE_NON_INITIALIZED = 0x02;
    public static final short STATE_INITIALIZING = 0x20;
    public static final short STATE_INITIALIZED = 0x21;
    private static final int MAX_LATELY_COUNT = 100; // 最大历史播放记录个数

    // 当前provider状态
    private volatile short mCurrentState = STATE_NON_INITIALIZED;
    // 默认分类
    private final ConcurrentMap<String, MusicModel> mMusicListById;
    // 收藏/喜欢列表
    private final List<String> mFavoriteTracks;
    // 关注列表
    private final List<String> mFollowTracks;
    // 最近播放分类
    private final List<String> mLatelyTracks;
    // 下载记录分类
//    private final Set<DownloadMusicModel> mDownloadTracks;
    private final List<DownloadMusicModel> mDownloadTracks;
    // 播放列队
//    private final Set<MusicModel> mPlayTracks;
    private final List<MusicModel> mPlayTracks;
    // 专辑分类
    private ConcurrentMap<AlbumModel, List<MusicModel>> mMusicListByAlbum;
    // 歌手分类
    private ConcurrentMap<ArtistModel, List<MusicModel>> mMusicListByArtist;
    // 文件夹分类
    private ConcurrentMap<String, List<MusicModel>> mMusicListByFolder;
    // 媒体文件类型分类
    private ConcurrentMap<String, List<MusicModel>> mMusicListByGenres;
    // 歌单分类
    private ConcurrentMap<SheetModel, List<MusicModel>> mMusicListBySheet;
    // 数据源
    private MusicSource mSource;
    private DataOperator mDbOperator;

    public MusicListProvider(Context context) {
        this(new LocalMusicSource(context));
    }

    public MusicListProvider(MusicSource source) {
        mSource = source;
        mDbOperator = new DBOperator(source.getContext(), source.getContext().getString(
                R.string.db_name), 1);
        mMusicListById = new ConcurrentHashMap<>();
        mMusicListByAlbum = new ConcurrentHashMap<>();
        mMusicListByArtist = new ConcurrentHashMap<>();
        mMusicListByFolder = new ConcurrentHashMap<>();
        mMusicListByGenres = new ConcurrentHashMap<>();
        mMusicListBySheet = new ConcurrentHashMap<>();
        mFavoriteTracks = new ArrayList<>();
        mFollowTracks = new ArrayList<>();
        mLatelyTracks = new ArrayList<>();
//        mDownloadTracks = Collections.newSetFromMap(new ConcurrentHashMap<DownloadMusicModel, Boolean>());
//        mPlayTracks = Collections.newSetFromMap(new ConcurrentHashMap<MusicModel, Boolean>());
        mDownloadTracks = new ArrayList<>();
        mPlayTracks = new ArrayList<>();
    }

    @Override
    public MusicModel getMusic(String musicId) {
        return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId) : null;
    }

    @Override
    public SheetModel getSheet(String title) {
        for (SheetModel sheetModel : mMusicListBySheet.keySet()) {
            if (sheetModel.getTitle().equals(title)) {
                return sheetModel;
            }
        }
        return null;
    }

    @Override
    public int getMusicTotal() {
        return mMusicListById.size();
    }

    @Override
    public Iterable<String> getGenres() {
        return mMusicListByGenres.keySet();
    }

    @Override
    public Iterable<AlbumModel> getAlbum() {
        return mMusicListByAlbum.keySet();
    }

    @Override
    public Iterable<AlbumModel> getAlbumByArtist(String artist) {
        List<AlbumModel> albumModels = new ArrayList<>();
        Set<AlbumModel> albums = mMusicListByAlbum.keySet();
        for (AlbumModel albumModel : albums) {
            if (albumModel.getArtist().contains(artist)) {
                albumModels.add(albumModel);
            }
        }
        return albumModels;
    }

    @Override
    public Iterable<ArtistModel> getArtist() {
        return mMusicListByArtist.keySet();
    }

    @Override
    public Iterable<String> getFolder() {
        return mMusicListByFolder.keySet();
    }

    @Override
    public Iterable<SheetModel> getSheet() {
        return getOrderSheets();
    }

    @Override
    public Iterable<ArtistModel> getFollow() {
        List<ArtistModel> artistModels = new ArrayList<>();
        for (String artistName : mFollowTracks) {
            artistModels.addAll((Collection<? extends ArtistModel>) searchArtistByName(artistName));
        }
        return artistModels;
    }

    @Override
    public Iterable<MusicModel> getLately() {
        List<MusicModel> musicModels = new ArrayList<>();
        for (String musicId : mLatelyTracks) {
            musicModels.add(getMusic(musicId));
        }
        return musicModels;
    }

    @Override
    public Iterable<DownloadMusicModel> getDownload() {
        return mDownloadTracks;
    }

    @Override
    public Iterable<MusicModel> getFavoriteMusic() {
        List<MusicModel> list = new ArrayList<>();
        for (String mFavoriteTrack : mFavoriteTracks) {
            list.add(mMusicListById.get(mFavoriteTrack));
        }
        return list;
    }

    @Override
    public Iterable<MusicModel> getPlayList() {
        return mPlayTracks;
    }

    @Override
    public Iterable<MusicModel> getMusicByGenre(String genre) {
        if (getCurrentState() != STATE_INITIALIZED || !mMusicListByGenres.containsKey(genre)) {
            return Collections.emptyList();
        }
        return mMusicListByGenres.get(genre);
    }

    @Override
    public Iterable<MusicModel> getMusicByFolder(String folderUrl) {
        if (getCurrentState() != STATE_INITIALIZED || !mMusicListByFolder.containsKey(folderUrl)) {
            return Collections.emptyList();
        }
        return mMusicListByFolder.get(folderUrl);
    }

    @Override
    public Iterable<MusicModel> getMusicByAlbum(String album) {
        if (getCurrentState() != STATE_INITIALIZED) {
            return Collections.emptyList();
        }
        for (AlbumModel albumModel : mMusicListByAlbum.keySet()) {
            if (albumModel.getName().equals(album)) {
                return mMusicListByAlbum.get(albumModel);
            }
        }
        return Collections.emptyList();
    }

    @Override
    public Iterable<MusicModel> getMusicByArtist(String artist) {
        if (getCurrentState() != STATE_INITIALIZED) {
            return Collections.emptyList();
        }
        for (ArtistModel artistModel : mMusicListByArtist.keySet()) {
            if (artistModel.getName().equals(artist)) {
                return mMusicListByArtist.get(artistModel);
            }
        }
        return Collections.emptyList();
    }

    @Override
    public Iterable<MusicModel> getMusicBySheet(SheetModel sheetModel) {
        return mMusicListBySheet.get(sheetModel);
    }

    @Override
    public int getIndexFromPlayList(String musicId) {
        int index = 0;
        for (MusicModel musicModel : mPlayTracks) {
            if (musicModel.getId().equals(musicId)) {
                break;
            }
            index++;
        }
        return index;
    }

    @Override
    public boolean setFavorite(String musicId) {
        if (!isFavorite(musicId)) {
            mFavoriteTracks.add(musicId);
            mDbOperator.addFavorite(musicId);
            return true;
        } else {
            mFavoriteTracks.remove(musicId);
            mDbOperator.deleteFavorite(musicId);
            return false;
        }
    }

    @Override
    public boolean setFollow(String artistName) {
        if (!isFollow(artistName)) {
            mFollowTracks.add(artistName);
            mDbOperator.addFollow(artistName);
            return true;
        } else {
            mFollowTracks.remove(artistName);
            mDbOperator.deleteFollow(artistName);
            return false;
        }
    }

    @Override
    public boolean setLately(String musicId) {
        if (hasLately(musicId)) {
            mLatelyTracks.remove(musicId);
            mDbOperator.deleteLately(musicId);
        }
        mLatelyTracks.add(musicId);
        mDbOperator.addLately(musicId);
        // 超过指定个数则删除最先加入的记录（限制历史记录个数）
        if (mLatelyTracks.size() > MAX_LATELY_COUNT) {
            mDbOperator.deleteLately(mLatelyTracks.get(0));
            mLatelyTracks.remove(0);
        }
        return true;
    }

    @Override
    public String setSheet(SheetModel sheetModel) {
        if (hasSheet(sheetModel)) {
            // 已存在相同歌单名的歌单
            return null;
        }
        sheetModel.setId(mDbOperator.addSheet(sheetModel));
        putSheet(sheetModel);
        return sheetModel.getId();
    }

    @Override
    public boolean setDownLoad(DownloadMusicModel downloadMusicModel) {
        if (hasDownloadMusic(downloadMusicModel)) {
            // 已存在相同数据源的下载记录
            return false;
        }
        mDownloadTracks.add(downloadMusicModel);
        mDbOperator.addDownloadMusic(downloadMusicModel);
        return true;
    }

    @Override
    public boolean setPlayMusic(String musicId) {
//        if (hasPlayMusic(musicId)) {
//            return false;
//        }
        mPlayTracks.add(getMusic(musicId));
        mDbOperator.addMusicToPlayList(musicId);
        return true;
    }

    @Override
    public void setPlayMusic(String musicId, int index) {
//        if (hasPlayMusic(musicId)) {
//            mPlayTracks.remove(getMusic(musicId));
//            mDbOperator.deleteMusicFromPlayList(musicId);
//        }
        if (index > mPlayTracks.size()) {
            mPlayTracks.add(getMusic(musicId));
        } else {
            mPlayTracks.add(index, getMusic(musicId));
        }
        mDbOperator.addMusicToPlayList(musicId);
    }

    @Override
    public void removeMusic(String musicId) {
        mMusicListById.remove(musicId);
        updateList();
    }

    @Override
    public void removeSheet(SheetModel sheetModel) {
        mMusicListBySheet.remove(sheetModel);
        mDbOperator.deleteSheet(sheetModel.getId());
    }

    @Override
    public void removeLately(String musicId) {
        mLatelyTracks.remove(musicId);
        mDbOperator.deleteLately(musicId);
    }

    @Override
    public void removeDownload(DownloadMusicModel downloadMusicModel) {
        mDownloadTracks.remove(downloadMusicModel);
        mDbOperator.deleteDownloadMusic(downloadMusicModel.getLocalUrl());
    }

    @Override
    public void removePlayMusic(String musicId) {
        mPlayTracks.remove(getMusic(musicId));
        mDbOperator.deleteMusicFromPlayList(musicId);
    }

    @Override
    public void clearLately() {
        mLatelyTracks.clear();
        mDbOperator.clearLately();
    }

    @Override
    public void clearPlayList() {
        mPlayTracks.clear();
        mDbOperator.clearPlayList();
    }

    @Override
    public boolean editSheet(SheetModel oldSheet, SheetModel newSheet) {
        if (hasSheet(newSheet)) {
            return false;
        }
        mDbOperator.updateSheet(newSheet);
        mMusicListBySheet.clear();
        initMusicListBySheet();
        return true;
    }

    @Override
    public void editSheetAddMusic(SheetModel sheetModel, List<String> musicId) {
        mMusicListBySheet.remove(sheetModel);
        sheetModel.setMusicIdList(musicId);
        putSheet(sheetModel);
        mDbOperator.updateSheet(sheetModel);
    }

    @Override
    public Iterable<MusicModel> searchAllMusic() {
        return mMusicListById.values();
    }

    @Override
    public Iterable<MusicModel> searchMusicBySongTitle(String title) {
        return searchMusic(MusicModel.MODEL_KEY_TITLE, title);
    }

    @Override
    public Iterable<MusicModel> searchMusicByAlbum(String album) {
        return searchMusic(MusicModel.MODEL_KEY_ALBUM, album);
    }

    @Override
    public Iterable<MusicModel> searchMusicByArtist(String artist) {
        return searchMusic(MusicModel.MODEL_KEY_ARTIST, artist);
    }

    @Override
    public Iterable<AlbumModel> searchAlbumByName(String name) {
        if (getCurrentState() != STATE_INITIALIZED) {
            return Collections.emptyList();
        }
        name = name.toLowerCase(Locale.CHINA);
        List<AlbumModel> albumList = new ArrayList<>();
        for (AlbumModel albumModel : mMusicListByAlbum.keySet()) {
            if (albumModel.getName().toLowerCase(Locale.CHINA).contains(name)) {
                albumList.add(albumModel);
            }
        }
        return albumList;
    }

    @Override
    public Iterable<ArtistModel> searchArtistByName(String name) {
        if (getCurrentState() != STATE_INITIALIZED) {
            return Collections.emptyList();
        }
        name = name.toLowerCase(Locale.CHINA);
        List<ArtistModel> artistList = new ArrayList<>();
        for (ArtistModel artistModel : mMusicListByArtist.keySet()) {
            if (artistModel.getName().toLowerCase(Locale.CHINA).contains(name)) {
                artistList.add(artistModel);
            }
        }
        return artistList;
    }

    @Override
    public boolean isFavorite(String musicId) {
        return mFavoriteTracks.contains(musicId);
    }

    @Override
    public boolean isFollow(String artistName) {
        return mFollowTracks.contains(artistName);
    }

    @Override
    public boolean isInitialized() {
        return getCurrentState() == STATE_INITIALIZED;
    }

    @Override
    public void retrieveMediaAsync(final ProviderCallback callback) {
        // 检测初始化状态
        if (getCurrentState() == STATE_INITIALIZED) {
            if (callback != null) {
                callback.onMusicCatalogReady(true);
            }
            return;
        }

        // 异步加载列表数据
        new AsyncTask<Void, Void, Short>() {
            @Override
            protected Short doInBackground(Void... params) {
                // 加载数据
                retrieveMedia();
                return getCurrentState();
            }

            @Override
            protected void onPostExecute(Short current) {
                // 回调完成事件
                if (callback != null) {
                    callback.onMusicCatalogReady(current == STATE_INITIALIZED);
                }
            }
        }.execute();
    }

    /**
     * 初始化列表数据
     */
    private synchronized void retrieveMedia() {
        try {
            if (getCurrentState() == STATE_NON_INITIALIZED) {
                setCurrentState(STATE_INITIALIZING);

                Iterator<MusicModel> tracks = mSource.iterators();
                while (tracks.hasNext()) {
                    MusicModel music = tracks.next();
                    mMusicListById.put(music.getId(), music);
                }
                // 创建分类列表
                buildClassifiedList();
            }
        } finally {
            if (getCurrentState() != STATE_INITIALIZED) {
                // 列表加载结束后调整状态
                setCurrentState(STATE_INITIALIZED);
            }
        }
    }

    private synchronized void buildClassifiedList() {
        // 初始化音乐分类
        updateList();
        initPlayList();

        // 初始化喜欢的、关注、最近播放和下载记录的列表
        mFavoriteTracks.addAll(mDbOperator.getFavorite());
        mFollowTracks.addAll(mDbOperator.getFollow());
        mLatelyTracks.addAll(mDbOperator.getLately());
        mDownloadTracks.addAll(mDbOperator.getDownloadMusic());

        // 初始化歌单
        initMusicListBySheet();
    }

    /**
     * 更新列表数据
     */
    private void updateList() {
        ConcurrentMap<AlbumModel, List<MusicModel>> newMusicListByAlbum = new ConcurrentHashMap<>();
        ConcurrentMap<ArtistModel, List<MusicModel>> newMusicListByArtist = new ConcurrentHashMap<>();
        ConcurrentMap<String, List<MusicModel>> newMusicListByFolder = new ConcurrentHashMap<>();

        for (MusicModel music : mMusicListById.values()) {
            newMusicListByAlbum = initAlbumList(newMusicListByAlbum, music);
            newMusicListByArtist = initArtistList(newMusicListByArtist, music);
            newMusicListByFolder = initFolderList(newMusicListByFolder, music);
        }

        mMusicListByAlbum = newMusicListByAlbum;
        mMusicListByArtist = newMusicListByArtist;
        mMusicListByFolder = newMusicListByFolder;
    }

    /**
     * 初始化播放列表
     */
    private void initPlayList() {
        for (String id : mDbOperator.getPlayList()) {
            mPlayTracks.add(getMusic(id));
        }
    }

    /**
     * 获取歌单及歌单中的歌曲
     */
    private void initMusicListBySheet() {
        List<SheetModel> sheetModelList = mDbOperator.getSheet();
        for (SheetModel sheetModel : sheetModelList) {
            putSheet(sheetModel);
        }
    }

    private void putSheet(SheetModel sheetModel) {
        List<MusicModel> musicList = new ArrayList<>();
        for (String musicId : sheetModel.getMusicIdList()) {
            musicList.add(getMusic(musicId));
        }
        mMusicListBySheet.put(sheetModel, musicList);
    }

    /**
     * 获取专辑
     */
    private ConcurrentMap<AlbumModel, List<MusicModel>> initAlbumList(
            ConcurrentMap<AlbumModel, List<MusicModel>> newMusicList, MusicModel music) {
        // 由于ConcurrentHashMap调用的是头部引用，所以只需要在引用的集合后面添加数据即可
        List<MusicModel> list = null;
        for (AlbumModel albumModel : newMusicList.keySet()) {
            // 是否存在,若存在则存入集合
            if (albumModel.getName().equals(music.getAlbum())) {
                list = newMusicList.get(albumModel);
                list.add(music);
                break;
            }
        }
        // 是否已经添加
        if (list != null) {
            return newMusicList;
        }
        list = new ArrayList<>();
        newMusicList.put(new AlbumModel(music.getAlbum(), music.getArtist(), music.getUrl(), music.getId()), list);
        list.add(music);
        return newMusicList;
    }

    /**
     * 获取艺术家
     */
    private ConcurrentMap<ArtistModel, List<MusicModel>> initArtistList(
            ConcurrentMap<ArtistModel, List<MusicModel>> newMusicList, MusicModel music) {
        List<MusicModel> list = null;
        for (ArtistModel artistModel : newMusicList.keySet()) {
            // 是否存在,若存在则存入集合
            if (artistModel.getName().equals(music.getArtist())) {
                list = newMusicList.get(artistModel);
                list.add(music);
                break;
            }
        }
        // 是否已经添加
        if (list != null) {
            return newMusicList;
        }
        list = new ArrayList<>();
        newMusicList.put(new ArtistModel(music.getArtist(), music.getImageUrl()), list);
        list.add(music);
        return newMusicList;
    }

    /**
     * 获取音乐文件夹
     */
    private ConcurrentMap<String, List<MusicModel>> initFolderList(
            ConcurrentMap<String, List<MusicModel>> newMusicList, MusicModel music) {
        // 由于ConcurrentHashMap调用的是头部引用，所以只需要在引用的集合后面添加数据即可
        List<MusicModel> list = null;
        String folderUrl = ConverterHelper.INSTANCE.getFolderFromUrl(music.getUrl());
        for (String folder : newMusicList.keySet()) {
            if (folder.equals(folderUrl)) {
                list = newMusicList.get(folder);
                list.add(music);
                break;
            }
        }
        if (list != null) {
            return newMusicList;
        }
        list = new ArrayList<>();
        newMusicList.put(folderUrl, list);
        list.add(music);
        return newMusicList;
    }

    /**
     * @param initState One of {@link #STATE_INITIALIZED}, {@link #STATE_INITIALIZING},
     *                  {@link #STATE_NON_INITIALIZED}
     */
    private void setCurrentState(short initState) {
        mCurrentState = initState;
    }

    /**
     * @return current state
     */
    private short getCurrentState() {
        return mCurrentState;
    }

    /**
     * 模糊搜索歌曲文件
     *
     * @param modelKey One of {@link MusicModel#MODEL_KEY_ALBUM},
     *                 {@link MusicModel#MODEL_KEY_ARTIST},{@link MusicModel#MODEL_KEY_URL},
     *                 {@link MusicModel#MODEL_KEY_TITLE} 搜索类型
     * @param query    条件
     * @return list
     */
    private Iterable<MusicModel> searchMusic(short modelKey, String query) {
        if (getCurrentState() != STATE_INITIALIZED) {
            return Collections.emptyList();
        }
        ArrayList<MusicModel> musicModels = new ArrayList<>();
        query = query.toLowerCase(Locale.CHINA);
        for (MusicModel music : mMusicListById.values()) {
            if (music.getString(modelKey).toLowerCase(Locale.CHINA).contains(query)) {
                musicModels.add(music);
            }
        }
        return musicModels;
    }

    /**
     * @param musicId id
     * @return 是否是已存在的播放记录
     */
    private boolean hasLately(String musicId) {
        for (String id : mLatelyTracks) {
            if (id.equals(musicId)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param sheetModel sheet
     * @return 是否是已存在的歌单
     */
    private boolean hasSheet(SheetModel sheetModel) {
        for (SheetModel sheet : mMusicListBySheet.keySet()) {
            if (sheet.getTitle().equals(sheetModel.getTitle())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param download 需要存入的下载记录
     * @return 是否已存在相同地址的下载歌曲
     */
    private boolean hasDownloadMusic(DownloadMusicModel download) {
        for (DownloadMusicModel downloadMusicModel : mDownloadTracks) {
            if (downloadMusicModel.getNetUrl().equals(download.getNetUrl())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param musicId id
     * @return 播放列队是否已存在该歌曲
     */
    private boolean hasPlayMusic(String musicId) {
        for (MusicModel musicModel : mPlayTracks) {
            if (musicId.equals(musicModel.getId())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @return 返回排序完成的list
     */
    private Iterable<SheetModel> getOrderSheets() {
        List<SheetModel> list = new ArrayList<>();
        list.addAll(mMusicListBySheet.keySet());
        Collections.sort(list);
        return list;
    }
}
